package jmg

import (
	"encoding/json"
	"fmt"
	nest_cache "lol.com/feb214-game/nest.git/cache"
	"lol.com/feb214-game/nest.git/cricket"
	"lol.com/feb214-game/nest.git/log"
	"lol.com/feb214-game/nest.git/mg"
	"lol.com/feb214-game/nest.git/tools/tz"
	"lol.com/feb214-game/nest.git/user"
	"math/rand"
	"strconv"
	"talent.com/cube/common/cache"
	"talent.com/cube/common/cxt"
	"talent.com/cube/common/msg"
	"talent.com/cube/conf"
	"talent.com/cube/conf/g"
	"time"
)

type globalInfo struct {
	TotalTax            int64   // 总抽水额
	WaterPool           int64   // 系统水池（营收）
	loseIndex           []int32 // 输家
	isAdjust            bool    // 是否换过牌
	totalUserBet        int64   // 真实用户总下注
	totalUserAward      int64   // 真实用户总奖励
	UserIndexAwards     map[uint64]map[int32]int64
	UserIndexBets       map[int32]int64
	UserIndexBetAccount map[int32]int32
	toBroadcast         []map[string]interface{} // 闪告列表
}

func getWinIndex(newBetRate map[int32]*HitRateConf) ([]int32, map[int32]int32) {
	var sumRate int32
	for _, rate := range newBetRate {
		sumRate += rate.Rate
	}
	ra := rand.Int31n(sumRate)

	winIndex := make([]int32, 0)
	for _, rate := range newBetRate {
		if ra < rate.Rate {
			for index, _ := range rate.DiceIndexMap {
				winIndex = append(winIndex, index)
			}
			return winIndex, rate.DiceIndexMap
		}
		ra -= rate.Rate
	}
	return nil, nil
}

// 随机赢家
func randomWinIndexesByStrategy(winInfo *msg.JmgST, globalInfo *globalInfo, strategyRate map[int32]*HitRateConf) {
	winIndex, diceIndexMap := getWinIndex(strategyRate)
	newDiceIndexArray := make(map[int32]int32)
	//找出还没有使用的区域
	for index, _ := range HitIndexConf {
		if _, ok := diceIndexMap[index]; !ok {
			newDiceIndexArray[index] = index
		}
	}

	//计算除开中奖的骰子外，还需要填补几个骰子
	totalUseNum := 0
	for _, num := range diceIndexMap {
		totalUseNum += int(num)
	}
	needNum := len(HitIndexConf) - totalUseNum
	if needNum > 0 {
		temp := 0
		for index, _ := range newDiceIndexArray {
			if temp >= needNum {
				break
			}
			diceIndexMap[index] = 1
			temp++
		}
	}

	winInfo.DiceIndex = diceIndexMap
	winInfo.WinIndex = winIndex
	for diceIndex, _ := range HitIndexConf {
		if diceIndexMap[diceIndex] >= 2 {
			winIndex = append(winIndex, diceIndex)
		} else {
			globalInfo.loseIndex = append(globalInfo.loseIndex, diceIndex)
		}
	}
}

// 随机赢家
func randomWinIndexes(winInfo *msg.JmgST, globalInfo *globalInfo) {
	//随机赢家
	var sumRate int32
	for _, value := range HitIndexConf {
		sumRate += value
	}
	diceMap := make(map[int32]int32)
	for i := 0; i < len(HitIndexConf); i++ {
		diceIndex := RandWinIndex(HitIndexConf, sumRate)
		if _, ok := diceMap[diceIndex]; ok {
			diceMap[diceIndex] = diceMap[diceIndex] + 1
		} else {
			diceMap[diceIndex] = 1
		}
	}
	winIndex := make([]int32, 0)
	for diceIndex, _ := range HitIndexConf {
		if diceMap[diceIndex] >= 2 {
			winIndex = append(winIndex, diceIndex)
		} else {
			globalInfo.loseIndex = append(globalInfo.loseIndex, diceIndex)
		}
	}
	winInfo.DiceIndex = diceMap
	winInfo.WinIndex = winIndex
}

// 计算奖励总额
func calcCurrentAward(tc *cxt.TableContext, winIndex int32) (int64, int64) {
	var (
		currentAward int64 // 奖励总额
		totalBet     int64 // 下注总额
		rate         int64 // 赔率
	)
	// 处理每个索引位置下注的结果
	for betIndex, betAmount := range tc.RealIndexBetCache {
		totalBet += betAmount
		// 命中
		if betIndex == winIndex {
			rate = int64(payRateConf[int32(betIndex)])
			currentAward += betAmount * rate
		}
	}
	return currentAward, totalBet
}

// 计算赢家数据
func calcWin(tc *cxt.TableContext) (*msg.JmgST, *globalInfo) {
	winInfo := &msg.JmgST{
		DiceIndex:  make(map[int32]int32),
		WinIndex:   make([]int32, 0),
		WinHistory: tc.Jmt.WinHistory,
	}
	globalInfo := &globalInfo{}
	//真实用户总下注
	totalReal := int64(0)
	for _, ust := range tc.RealIndexBetCache {
		totalReal += ust
	}
	if totalReal > 0 {
		userStats := g.StatsDAO.GetUserStats(tc.RealUserId)
		user, _ := g.GetAccountDAO(g.Jmg).GetUser(tc.RealUserId)
		recharge, _, _, _ := rechargePer(userStats, user)
		hour := time.Now().Sub(userStats.CreatedAt).Hours()

		var rechargeUserJson []conf.StrategyConfNew
		strategyConf := conf.StrategyConfNew{}
		ru := g.Viper.GetString("RechargeUser")
		json.Unmarshal([]byte(ru), &rechargeUserJson)
		for _, value := range rechargeUserJson {
			if hour >= float64(value.UserDay[0]*24) && hour < float64(value.UserDay[1]*24) &&
				tc.RechargeRate >= value.RechargeWithdrawalRate[0] && tc.RechargeRate < value.RechargeWithdrawalRate[1] {
				tc.CheatId = g.RechargeUser
				strategyConf = value
				strategyConf.Hit = true
				strategyConf.UserId = tc.RealUserId
				tc.StrategyConf = strategyConf
				break

			}
		}

		if totalReal >= recharge {
			var betMoreThanRechargeJson conf.StrategyConfNew
			bmtr := g.Viper.GetString("BetMoreThanRecharge")
			json.Unmarshal([]byte(bmtr), &betMoreThanRechargeJson)
			tc.CheatId = g.BetMoreThanRecharge
			strategyConf = betMoreThanRechargeJson
			strategyConf.Hit = true
			strategyConf.UserId = tc.RealUserId
			tc.StrategyConf = strategyConf
		}

		var sumRate int32
		for _, value := range HitIndexConf {
			sumRate += value
		}
		diceMap := make(map[int32]int32)
		for i := 0; i < len(HitIndexConf); i++ {
			diceIndex := RandWinIndex(HitIndexConf, sumRate)
			if _, ok := diceMap[diceIndex]; ok {
				diceMap[diceIndex] = diceMap[diceIndex] + 1
			} else {
				diceMap[diceIndex] = 1
			}
		}
		if len(diceMap) < 6 {
			hitAwardDiceArray := make([]int32, 0)
			for _, num := range diceMap {
				if num > 1 {
					hitAwardDiceArray = append(hitAwardDiceArray, num)
				}
			}
			strategyRate := make(map[int32]*HitRateConf)
			//中一个图案的情况
			if len(hitAwardDiceArray) == 1 {
				for index, _ := range HitIndexConf {
					diceIndexMap := make(map[int32]int32)
					diceIndexMap[index] = hitAwardDiceArray[0]
					conf := &HitRateConf{
						DiceIndexMap: diceIndexMap,
						Rate:         100,
					}
					if betAmount, ok := tc.RealIndexBetCache[index]; ok {
						//盈利大于下注
						if totalReal < betAmount*int64(payRateConf[hitAwardDiceArray[0]]) {
							if hitAwardDiceArray[0] == 6 {
								// 6个图案一起的 100倍的 永远几率为0
								conf.Rate = 0
							} else {
								//其他的就正常降权
								conf.Rate = int32(float64(conf.Rate) * float64(100+tc.StrategyConf.Rate) / float64(100))
							}

						}
					}
					strategyRate[index] = conf
				}
			} else if len(hitAwardDiceArray) == 2 {
				tempIndex := int32(0)
				for i, _ := range HitIndexConf {
					diceIndexMap := make(map[int32]int32)
					diceIndexMap[i] = hitAwardDiceArray[0]

					finalAward := int64(0)
					if betAmount, ok := tc.RealIndexBetCache[i]; ok {
						finalAward += betAmount * int64(payRateConf[hitAwardDiceArray[0]])
					}
					for k, _ := range HitIndexConf {
						if i == k {
							continue
						}
						diceIndexMap[k] = hitAwardDiceArray[1]
						if betAmount, ok := tc.RealIndexBetCache[k]; ok {
							finalAward += betAmount * int64(payRateConf[hitAwardDiceArray[1]])
						}
						conf := &HitRateConf{
							DiceIndexMap: diceIndexMap,
							Rate:         100,
						}
						//盈利大于下注
						if totalReal < finalAward {
							//正常降权
							conf.Rate = int32(float64(conf.Rate) * float64(100+tc.StrategyConf.Rate) / float64(100))
						}
						strategyRate[tempIndex] = conf
						tempIndex++
					}
				}
			} else if len(hitAwardDiceArray) == 3 {
				tempIndex := int32(0)
				for i, _ := range HitIndexConf {
					diceIndexMap := make(map[int32]int32)
					diceIndexMap[i] = hitAwardDiceArray[0]

					finalAward := int64(0)
					if betAmount, ok := tc.RealIndexBetCache[i]; ok {
						finalAward += betAmount * int64(payRateConf[hitAwardDiceArray[0]])
					}
					for k, _ := range HitIndexConf {
						if i == k {
							continue
						}
						diceIndexMap[k] = hitAwardDiceArray[1]
						if betAmount, ok := tc.RealIndexBetCache[k]; ok {
							finalAward += betAmount * int64(payRateConf[hitAwardDiceArray[1]])
						}
						for j, _ := range HitIndexConf {
							if j == i || j == k {
								continue
							}
							diceIndexMap[j] = hitAwardDiceArray[2]
							if betAmount, ok := tc.RealIndexBetCache[j]; ok {
								finalAward += betAmount * int64(payRateConf[hitAwardDiceArray[2]])
							}

							conf := &HitRateConf{
								DiceIndexMap: diceIndexMap,
								Rate:         100,
							}
							//盈利大于下注
							if totalReal < finalAward {
								//正常降权
								conf.Rate = int32(float64(conf.Rate) * float64(100+tc.StrategyConf.Rate) / float64(100))
							}
							strategyRate[tempIndex] = conf
							tempIndex++
						}
					}
				}
			}
			randomWinIndexesByStrategy(winInfo, globalInfo, strategyRate)
		} else {
			//如果骰子的图案等于6个，等于没有赢家
			winIndex := make([]int32, 0)
			for diceIndex, _ := range HitIndexConf {
				globalInfo.loseIndex = append(globalInfo.loseIndex, diceIndex)
			}
			winInfo.DiceIndex = diceMap
			winInfo.WinIndex = winIndex
		}

	} else {
		// 随机赢家
		randomWinIndexes(winInfo, globalInfo)
	}
	// 奖池数量
	dailyBet, _ := cache.GetDailyBet(g.Jmg)
	dailyAward, _ := cache.GetDailyAward(g.Jmg)
	globalInfo.WaterPool = dailyBet - dailyAward
	var cardsBefore = ""
	cardsBefore = winInfo.PrettyString()
	if tc.StrategyConf.Hit {
		announceTrack(tc, globalInfo, totalReal, cardsBefore, cardsBefore, strconv.Itoa(int(tc.CheatId)), true, true)
	}

	return winInfo, globalInfo
}

func announceTrack(tc *cxt.TableContext, globalInfo *globalInfo, totalReal int64, carsJson string, cardsBefore string, strategyId string, match bool, hit bool) {
	indexAward := make(map[int32]int64, 0)
	//cardsJson := tc.Cricket.WinInfo.PrettyString()
	//真实用户输赢
	for uid, indexAwards := range globalInfo.UserIndexAwards {
		player := tc.Players.GetPlayer(uid)
		if player != nil && !player.IsRobot() {
			//只计算机器人输赢
			for index, award := range indexAwards {
				indexAward[index] += award
			}
		}
	}
	storeTrack(tc, totalReal, carsJson, cardsBefore, tc.RealIndexBetCache, indexAward, strategyId, hit, match)
}

// 发奖
func dispatchAwards(tc *cxt.TableContext, winInfo *msg.JmgST, globalInfo *globalInfo) {
	var (
		player   *cxt.UserContext
		chipInfo *msg.ChipInfo
		jmgConf  = conf.JmgConfig().Game
		minAward = g.StatsDAO.GetBroadcastMinAmount("jmg") * g.CoinRate // 闪告相关

		rate             int64 // 赔率
		betIndex         int32 // 下注区域
		playerTotalTax   int64 // 单个用户的总税收
		playerTotalAward int64 // 单个用户的总奖励

		betCount int32
	)
	globalInfo.UserIndexAwards = make(map[uint64]map[int32]int64)
	globalInfo.UserIndexBets = map[int32]int64{0: int64(0), 1: int64(0), 2: int64(0), 3: int64(0), 4: int64(0), 5: int64(0)}
	globalInfo.UserIndexBetAccount = map[int32]int32{0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0}

	// 计算每个用户的奖励
	for playerId, playerCost := range tc.CostCache {
		player = tc.Players.GetPlayer(playerId)
		if player == nil {
			log.Info("nil uid:%v costCache:%v", playerId, playerCost)
			continue
		}
		playerTotalAward = 0
		playerTotalTax = 0
		globalInfo.UserIndexAwards[playerId] = make(map[int32]int64, len(player.BetInfo))

		// 👇处理每处下注的命中情况
		for betIndex, chipInfo = range player.BetInfo {
			var indexAward int64
			// 命中
			if num, ok := tc.Jmt.DiceIndex[betIndex]; ok {
				if num >= 2 {
					rate = int64(payRateConf[num])
					// 当前下注区域总奖励、真实奖励、税收
					indexTotalAward := chipInfo.Total * (rate)
					indexAward = int64(float64(indexTotalAward-chipInfo.Total)*jmgConf.AfterTaxRate) + chipInfo.Total // 交税后

					playerTotalAward += indexAward
					playerTotalTax += indexTotalAward - indexAward
				}
			}
			globalInfo.UserIndexAwards[playerId][betIndex] = indexAward
		}
		// 发奖
		player.Award += playerTotalAward
		player.Credit += playerTotalAward
		player.Tax += playerTotalTax

		//闪告, 异步化
		if playerTotalAward > minAward {
			globalInfo.toBroadcast = append(globalInfo.toBroadcast, map[string]interface{}{
				"GameId":     "jmg",
				"GameType":   "Jhandi Munda",
				"UserId":     playerId,
				"PlayerName": player.UserName,
				"GoldCount":  fmt.Sprintf("%.2f", float64(playerTotalAward)/g.CoinRate),
			})
		}
		// 真实用户
		if !player.IsRobot() {
			for index, chipInfo := range player.BetInfo {
				globalInfo.UserIndexBets[index] += chipInfo.Total
				globalInfo.UserIndexBetAccount[index]++
				betCount++
			}
			globalInfo.totalUserBet += playerCost
			globalInfo.totalUserAward += player.Award // 真实用户的总奖励，也就是抽水
			globalInfo.TotalTax += playerTotalTax
			nest_cache.StreamTrack(g.KafkaPool, g.TopicCommon, strconv.Itoa(int(tc.TermNumber)), cricket.AwardTrack{
				Type:       "award",
				Ts:         time.Now().Unix(),
				UserId:     playerId,
				TableId:    tc.ID,
				Info:       globalInfo.UserIndexAwards[playerId],
				Total:      player.Award,
				Bet:        playerCost,
				Award:      player.Award,
				After:      player.Credit,
				Tax:        player.Tax,
				RoomIndex:  tc.Kind,
				TermNumber: tc.TermNumber,
				RobotLevel: player.RobotLevel,
				GameType:   tc.GameType,
				Pkg:        player.Pkg,
			})

			var wlist []interface{}
			wlist = append(wlist, player.BetInfo)
			wlist = append(wlist, payRateConf)
			wlist = append(wlist, winInfo.PrettyJson())
			userBets, _ := json.Marshal(wlist)
			nest_cache.StreamTrack(g.KafkaPool, g.TopicCommon, strconv.Itoa(int(tc.TermNumber)), cricket.UserStreamTrack{
				Type:    "user",
				Ts:      time.Now().Unix(),
				UserId:  playerId,
				Term:    tc.TermNumber,
				Bet:     playerCost,
				Award:   player.Award,
				Credit:  player.Credit,
				Tax:     player.Tax,
				BetArea: string(userBets),
				Pkg:     player.Pkg,
			})
		}

		//log.Track("",
		//	zap.String("type", "award"),
		//	zap.Int32("game", g.Dxt),
		//	zap.Uint64("user_id", playerId),
		//	zap.Int64("table_id", tc.ID),
		//	zap.Int64("total", player.Award),
		//	zap.Any("info", indexAwards),
		//	zap.Int64("bet", playerCost),
		//	zap.Int64("after", player.Credit),
		//	zap.Int64("tax", player.Tax),
		//	zap.Int32("room_index", tc.Kind),
		//	zap.Int64("term_number", tc.TermNumber),
		//	zap.Int("robot_level", player.RobotLevel),
		//)
	}

	log.Info("游戏track：输赢记录：%v，", winInfo.PrettyString())
	if tc.UserCount > 0 && globalInfo.totalUserBet > 0 {
		//计算下注人数,各个区域牌型,下注总数，下注人数和输赢
		var betlist []interface{}
		betlist = append(betlist, globalInfo.UserIndexBets)
		betlist = append(betlist, winInfo.PrettyJson())
		betlist = append(betlist, globalInfo.UserIndexBetAccount)
		bytes, _ := json.Marshal(betlist)
		nest_cache.StreamTrack(g.KafkaPool, g.TopicCommon, strconv.Itoa(int(tc.TermNumber)), cricket.GameStreamTrack{
			Type:        "game",
			Ts:          time.Now().Unix(),
			Term:        tc.TermNumber,
			BankerAward: globalInfo.totalUserBet - globalInfo.totalUserAward,
			Tax:         globalInfo.TotalTax,
			BetArea:     string(bytes),
			UserCount:   tc.UserCount,
			BetTotalNum: betCount,
		})
	}
}

// 更新开奖历史
func freshWinHistory(tc *cxt.TableContext, winInfo *msg.JmgST) {
	diceMap := winInfo.DiceIndex
	for diceIndex, _ := range HitIndexConf {
		if len(tc.Jmt.WinHistory[diceIndex].WinHistoryIndex) >= g.WinHistorySize {
			tc.Jmt.WinHistory[diceIndex].WinHistoryIndex = tc.Jmt.WinHistory[diceIndex].WinHistoryIndex[1:]
		}
		if diceMap[diceIndex] >= 2 {
			tc.Jmt.WinHistory[diceIndex].WinHistoryIndex = append(tc.Jmt.WinHistory[diceIndex].WinHistoryIndex, diceMap[diceIndex])
		} else {
			tc.Jmt.WinHistory[diceIndex].WinHistoryIndex = append(tc.Jmt.WinHistory[diceIndex].WinHistoryIndex, 0)
		}
	}
}

// 奖励结算
func announce(w *world) *msg.AwardNT {
	tc := w.tc
	resp := &msg.AwardNT{
		Timestamp:  tz.GetNowTsMs(),
		TermNumber: tc.TermNumber, //globalInfo在外部填充
	}

	// 💯计算赢家信息
	winInfo, globalInfo := calcWin(tc)
	resp.Jmt = winInfo
	tc.Jmt = winInfo
	//freshCR(cr, newCR, winInfo, tc.Turn == 1)

	// 每进行20局以后，需要重置已有的对局记录和历史
	if tc.Turn >= g.WinHistorySize {
		tc.Turn = 1
	} else {
		tc.Turn++
	}

	// 更新房间生命周期
	updateTableLifeStats(tc)
	// 💯发放奖励
	dispatchAwards(tc, winInfo, globalInfo)

	// 💯更新开奖历史、牌路
	freshWinHistory(tc, winInfo)

	// 更新蓄水池
	cache.IncrDailyAward(g.Jmg, globalInfo.totalUserAward)

	// 闪告
	if len(globalInfo.toBroadcast) > 0 {
		im := g.GetPublicCache(g.Jmg)
		tc.Skeleton.Go(func() {
			for _, v := range globalInfo.toBroadcast {
				_ = im.Broadcast2IM(v)
			}
		}, nil)
	}
	//log.Track("",
	//	zap.String("type", "announce_game"),
	//	zap.Int32("game", g.Dxt),
	//	zap.Int64("table_id", tc.ID),
	//	zap.Int64("term_number", tc.TermNumber),
	//	zap.Int32("room_index", tc.Kind),
	//	zap.Int64("total_tax", globalInfo.TotalTax),
	//	zap.Int64("total_bet", globalInfo.totalUserBet),
	//	zap.Int64("total_award", globalInfo.totalUserAward),
	//	zap.Any("win_info", log.AnyObject(winInfo)),
	//	zap.Bool("is_adjust", globalInfo.isAdjust),
	//	zap.Int64("water_pool", globalInfo.WaterPool),
	//)

	log.Info("[jmg]announce term %v of table %v, info:%+v, userBet:%v, userAward:%v",
		tc.TermNumber, tc.ID, winInfo.PrettyString(),
		globalInfo.totalUserBet, globalInfo.totalUserAward)
	return resp
}

func storeTrack(tc *cxt.TableContext, totalReal int64, cardsJson string, cardsBefore string, bets map[int32]int64, awards map[int32]int64, strategyId string, hit bool, match bool) {
	if tc.UserCount > 0 {
		//记录各区域下注和各区域结算
		nest_cache.StreamTrack(g.KafkaPool, g.TopicCommon, strconv.Itoa(int(tc.TermNumber)), cricket.StrategySystem{
			Type:            "strategy",
			Ts:              time.Now().Unix(),
			Room:            tc.Kind,
			Table:           uint64(tc.ID),
			Term:            tc.TermNumber,
			UserNum:         tc.UserCount,
			Bets:            bets,
			Awards:          awards,
			UserAmount:      totalReal,
			HandCardsBefore: cardsBefore,
			HandCards:       cardsJson,
			StrategyId:      strategyId,
			Hit:             hit,
			Match:           match,
		})
	}
}

func updateTableLifeStats(tc *cxt.TableContext) {
	if tc.UserCount > 0 {
		tc.LastActiveAt = tz.GetNowTs()
	}
}

func rechargePer(userStats *mg.UserStats, user *user.Account) (int64, int64, float64, bool) {
	recharge := userStats.Recharge.Total
	withdraw := userStats.Withdraw.Total
	//recharge, withdraw := mg.GetAmountD(userStats)
	//提现中
	processing, _ := g.GetAccountDAO(g.Jmg).GetProcessingWithdraw(userStats.ID)
	//提现金额 = 已经提现 + 提现中 + 可提现
	finalWithdraw := withdraw + processing + user.WithdrawableAmount
	log.Info("userId:%v ,finalRecharge:%v,finalWithdraw:%v", user.ID, recharge, finalWithdraw)
	rechargeRate := float64(finalWithdraw) / float64(recharge)
	var isNoWithdraw bool
	//如果用户充值 >0 并且没有tx，就是 用户未完成首次提现
	if recharge > 0 && withdraw+processing == 0 {
		isNoWithdraw = true
	}
	return recharge, finalWithdraw, rechargeRate, isNoWithdraw
}

func RandWinIndex(betRate map[int32]int32, sumRate int32) int32 {
	ra := rand.Int31n(sumRate)
	for i, rate := range betRate {
		if ra < rate {
			return i
		}
		ra -= rate
	}
	return -1
}
